﻿using System.Collections.Generic;
using StructureMap;
using StructureMap.Attributes;

namespace Nexia.FluentHtmlElements.IoC
{
	public static class DependencyFactory
	{
		/// <summary>
		/// Get an concrete implementation of interface T.
		/// </summary>
		/// <typeparam name="T">The interface to get</typeparam>
		/// <returns>A class implementing interface T</returns>
		public static T GetInstance<T>()
		{
			Dependencies.Init();

			return ObjectFactory.GetInstance<T>();
		}

		/// <summary>
		/// Get all concrete implmentations of interface T.
		/// </summary>
		/// <typeparam name="T">The interface to get</typeparam>
		/// <returns>A class implementing interface T</returns>
		public static IList<T> GetAllInstances<T>()
		{
			Dependencies.Init();

			return ObjectFactory.GetAllInstances<T>();
		}

		/// <summary>
		/// Get an concrete implementation of interface T.
		/// </summary>
		/// <typeparam name="T">The interface to get</typeparam>
		/// <param name="name">The name of the instance</param>
		/// <returns>A class implementing interface T</returns>
		public static T GetNamedInstance<T>(string name)
		{
			Dependencies.Init();

			return ObjectFactory.GetNamedInstance<T>(name);
		}

		/// <summary>
		/// Register a concrete class that should be injected by default.
		/// </summary>
		/// <typeparam name="I">The interface</typeparam>
		/// <typeparam name="ConcreteClass">The default concrete class which implements I.</typeparam>
		public static void RegisterDefaultConcreteType<I, ConcreteClass>() where ConcreteClass : class, I
		{
			ObjectFactory.Configure(x => x.ForRequestedType<I>().TheDefaultIsConcreteType<ConcreteClass>());
		}

		/// <summary>
		/// Register a named concrete class that should be injected when requested a I/name combination
		/// </summary>
		/// <typeparam name="I">The interface</typeparam>
		/// <typeparam name="ConcreteClass">The default concrete class which implements I.</typeparam>
		public static void RegisterNamedConcreteType<I, ConcreteClass>(string name) where ConcreteClass : class, I
		{
			ObjectFactory.Configure(x => x.ForRequestedType<I>().AddInstances(z => z.OfConcreteType<ConcreteClass>().WithName(name)));
		}

		/// <summary>
		/// Register a singleton concrete class that should be injected by default.
		/// </summary>
		/// <typeparam name="I">The interface</typeparam>
		/// <typeparam name="ConcreteClass">The default concrete class which implements I.</typeparam>
		public static void RegisterDefaultConcreteTypeSingleton<I, ConcreteClass>() where ConcreteClass : class, I
		{
			ObjectFactory.Configure(x => x.ForRequestedType<I>().TheDefaultIsConcreteType<ConcreteClass>().AsSingletons());
		}

		/// <summary>
		/// Register a cached concrete class that should be injected by default.
		/// The factory will get back the same class once it is cached.
		/// Used HttpContext if available, the ThreadLocalStorage otherwise.
		/// </summary>
		/// <typeparam name="I">The interface</typeparam>
		/// <typeparam name="ConcreteClass">The default concrete class which implements I.</typeparam>
		public static void RegisterCachedDefaultConcreteType<I, ConcreteClass>() where ConcreteClass : class, I
		{
			ObjectFactory.Configure(x => x.ForRequestedType<I>().TheDefaultIsConcreteType<ConcreteClass>().CacheBy(InstanceScope.Hybrid));
		}

		/// <summary>
		/// Removes all registrations.
		/// </summary>
		public static void Reset()
		{
			ObjectFactory.ResetDefaults();
		}

		/// <summary>
		/// When asking for T return instance.
		/// </summary>
		/// <typeparam name="T">The requested type</typeparam>
		/// <param name="instance">The instance to return</param>
		/// <returns>A class implementing interface T</returns>
		public static void Inject<T>(T instance)
		{
			Dependencies.Init();

			ObjectFactory.Inject(instance);
		}
	}
}
